Interacting with the Default Application Domain

Recall that when a .NET executable starts, the CLR will automatically place it into the default app domain o f the hosting process. This is done automatically and transparently, and you never have to author any specific code to do so. However, it is possible for your application to gain access to this default application domain using the static AppDomain.CurrentDomain property. Once you have this access point, you are able to hook into any events of interest, or make use of the methods and properties of AppDomain to perform some runtime diagnostics.

To learn how to interact with the default application domain, begin by creating a new Console Application named DefaultAppDomainApp. Now, update your Program with the following logic, which will simply display some details about the default application domain, using a number of members of the AppDomain class:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***** Fun with the default app domain *****\n");
        DisplayDADStats();
        Console.ReadLine();
    }

    private static void DisplayDADStats()
    {
        // Get access to the app domain for the current thread.
        AppDomain defaultAD = AppDomain.CurrentDomain;

        // Print out various stats about this domain.
        Console.WriteLine("Name of this domain: {0}", defaultAD.FriendlyName);
        Console.WriteLine("ID of domain in this process: {0}", defaultAD.Id);
        Console.WriteLine("Is this the default domain?: {0}",
            defaultAD.IsDefaultAppDomain());
        Console.WriteLine("Base directory of this domain: {0}", defaultAD.BaseDirectory);
    }
}

The output of this example can be seen here:

***** Fun with the default app domain *****

Name of this domain: DefaultAppDomainApp.exe
ID of domain in this process: 1
Is this the default domain?: True
Base directory of this domain: E:\MyCode\DefaultAppDomainApp\bin\Debug\

Notice that the name of the default application domain will be identical to the name of the executable which is contained within it (DefaultAppDomainApp.exe in this example). Also notice, that the base directory value, which will be used to probe for externally required private assemblies, maps to the current location of the deployed executable.

Enumerating Loaded Assemblies

It is also possible to discover all of the loaded .NET assemblies within a given application domain using the instance level GetAssemblies() method. This method will return to you an array of Assembly objects, which as you recall from the previous chapter, is a member of the System.Reflection namespace (so don’t forget to import this namespace into your C# code file!)

To illustrate, define a new method named ListAllAssembliesInAppDomain() within the Program class. This helper method will obtain all loaded assemblies, and print out the friendly name and version of each:

static void ListAllAssembliesInAppDomain()
{
    // Get access to the app domain for the current thread.
    AppDomain defaultAD = AppDomain.CurrentDomain;

    // Now get all loaded assemblies in the default app domain.
    Assembly[] loadedAssemblies = defaultAD.GetAssemblies();
    Console.WriteLine("***** Here are the assemblies loaded in {0} *****\n",
        defaultAD.FriendlyName);
    foreach(Assembly a in loadedAssemblies)
    {
        Console.WriteLine("-> Name: {0}", a.GetName().Name);
        Console.WriteLine("-> Version: {0}\n", a.GetName().Version);
    }
}

Assuming you have updated your Main() method to call this new member, you will see that the application domain hosting your executable is currently making use of the following .NET libraries:

***** Here are the assemblies loaded in DefaultAppDomainApp.exe *****

-> Name: mscorlib
-> Version: 4.0.0.0

-> Name: DefaultAppDomainApp
-> Version: 1.0.0.0

Now understand that the list of loaded assemblies can change at any time as you author new C# code. For example, assume you have updated your ListAllAssembliesInAppDomain() method to make use of a LINQ query, which will order the loaded assemblies by name:

static void ListAllAssembliesInAppDomain()
{
    // Get access to the app domain for the current thread.
    AppDomain defaultAD = AppDomain.CurrentDomain;

    // Now get all loaded assemblies in the default app domain.
    var loadedAssemblies = from a in defaultAD.GetAssemblies()
        orderby a.GetName().Name select a;

    Console.WriteLine("***** Here are the assemblies loaded in {0} *****\n",
        defaultAD.FriendlyName);
    foreach (var a in loadedAssemblies)
    {
        Console.WriteLine("-> Name: {0}", a.GetName().Name);
        Console.WriteLine("-> Version: {0}\n", a.GetName().Version);
    }
}

If you were to run the program once again, you will see that System.Core.dll and System.dll have also been loaded into memory, as they are required for the LINQ to Objects API:

***** Here are the assemblies loaded in DefaultAppDomainApp.exe *****

-> Name: DefaultAppDomainApp
-> Version: 1.0.0.0

-> Name: mscorlib
-> Version: 4.0.0.0

-> Name: System
-> Version: 4.0.0.0

-> Name: System.Core
-> Version: 4.0.0.0

Receiving Assembly Load Notifications

If you wish to be informed by the CLR when a new assembly has been loaded into a given application domain, you may handle the AssemblyLoad event. This event is typed against the AssemblyLoadEventHandler delegate, which can point to any method taking a System.Object as the first parameter, and an AssemblyLoadEventArgs as the second.

Let’s add one final method to the current Program class called InitDAD(). As the name suggests, this method will initialize the default application domain, specifically by handling the AssemblyLoad event via a fitting lambda expression:

private static void InitDAD()
{
    // This logic will print out the name of any assembly
    // loaded into the applicaion domain, after it has been
    // created.
    AppDomain defaultAD = AppDomain.CurrentDomain;
    defaultAD.AssemblyLoad += (o, s) =>
    {
        Console.WriteLine("{0} has been loaded!", s.LoadedAssembly.GetName().Name);
    };
}

As you would expect, when you run the modified application, you will be notified when a new assembly has been loaded. Here, you are simply printing out the friendly name of the assembly, using the LoadedAssembly property of the incoming AssemblyLoadedEventArgs parameter.

Source Code The DefaultAppDomainApp project is included under the Chapter 16 subdirectory.